package com.dashen.general.server.config.security;

import com.dashen.general.server.GeneralServerApplication;
import com.dashen.general.server.config.security.user.GeneralUserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.security.web.csrf.CookieCsrfTokenRepository;
import org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository;

/**
 * @author Barry Wang
 * 官方文档中文 https://springcloud.cc/spring-security-zhcn.html
 * 官方文档英文 https://docs.spring.io/spring-security/site/docs/5.0.5.RELEASE/reference/html5
 */
@EnableWebSecurity(debug = true)
public class GeneralWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {

    public GeneralWebSecurityConfigurerAdapter(){

    }

    @Override
    public void configure(WebSecurity web) throws Exception {
        super.configure(web);
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        //super.configure(auth);

        //构建 userDetailService
        auth.inMemoryAuthentication()
                .withUser("barry")
                .password(PasswordEncoderFactories.createDelegatingPasswordEncoder().encode("barry"))
                .roles("test");

        //auth.userDetailsService(customUserDetailsService());

    }

    /**
     *  在解析前，先说说两个至关重要的类：OncePerRequestFilter和GenericFilterBean，在过滤器链的过滤器中，或多或少间接或直接继承到
     *
     *  OncePerRequestFilter顾名思义，能够确保在一次请求只通过一次filter，而不需要重复执行。
     *  GenericFilterBean是javax.servlet.Filter接口的一个基本的实现类
     *      1.GenericFilterBean将web.xml中filter标签中的配置参数-init-param项作为bean的属性
     *      2.GenericFilterBean可以简单地成为任何类型的filter的父类
     *      3.GenericFilterBean的子类可以自定义一些自己需要的属性
     *      4.GenericFilterBean，将实际的过滤工作留给他的子类来完成，这就导致了他的子类不得不实现doFilter方法
     *      5.GenericFilterBean不依赖于Spring的ApplicationContext，Filters通常不会直接读取他们的容器信息（ApplicationContext concept）
     *        而是通过访问spring容器（Spring root application context）中的service beans来获取，
     *        通常是通过调用filter里面的getServletContext() 方法来获取
     *
     *   WebAsyncManagerIntegrationFilter
     *      表单认证是最常用的一个认证方式，一个最直观的业务场景便是允许用户在表单中输入用户名和密码进行登录，
     *      而这背后的UsernamePasswordAuthenticationFilter，在整个Spring Security的认证体系中则扮演着至关重要的角色
     *   SecurityContextPersistenceFilter
     *      1.先实例SecurityContextHolder->HttpSessionSecurityContextRepository（下面以repo代替）.
     *        作用：其会从Session中取出已认证用户的信息,提高效率,避免每一次请求都要查询用户认证信息。
     *      2.根据请求和响应构建HttpRequestResponseHolder
     *      3.repo根据HttpRequestResponseHolder加载context获取SecurityContext
     *      4.SecurityContextHolder将获得到的SecurityContext设置到Context中，然后继续向下执行其他过滤器
     *      5.finally-> SecurityContextHolder获取SecurityContext，然后清除，并将其和请求信息保存到repo，从请求中移除FILTER_APPLIED属性
     *   HeaderWriterFilter
     *      往该请求的Header中添加相应的信息,在http标签内部使用security:headers来控制
     *   CsrfFilter
     *      csrf又称跨域请求伪造，攻击方通过伪造用户请求访问受信任站点。
     *      对需要验证的请求验证是否包含csrf的token信息，如果不包含，则报错。这样攻击网站无法获取到token信息，则跨域提交的信息都无法通过过滤器的校验。
     *   LogoutFilter
     *      匹配URL,默认为/logout
     *      匹配成功后则用户退出,清除认证信息
     *   UsernamePasswordAuthenticationFilter
     *      表单认证是最常用的一个认证方式，一个最直观的业务场景便是允许用户在表单中输入用户名和密码进行登录，
     *      而这背后的UsernamePasswordAuthenticationFilter，在整个Spring Security的认证体系中则扮演着至关重要的角色
     *   DefaultLoginPageGeneratingFilter
     *      login page
     *   BasicAuthenticationFilter
     *      责处理HTTPHeader中的基本认证信息。
     *      工作原理：在header中获取特定key和特定形式的value（Authorization、Basic [Token]），获取的到，即使用当前过滤器进行验证身份信息。获取不到，则继续执行正常的过滤链。
     *      在使用无状态认证时，需要关闭CSRF。
     *   RequestCacheAwareFilter
     *      通过HttpSessionRequestCache内部维护了一个RequestCache，用于缓存HttpServletRequest
     *   SecurityContextHolderAwareRequestFilter
     *      针对ServletRequest进行了一次包装，使得request具有更加丰富的API
     *   RememberMeAuthenticationFilter
     *      RememberMe
     *   AnonymousAuthenticationFilter
     *      1.  当SecurityContextHolder中认证信息为空,则会创建一个匿名用户存入到SecurityContextHolder中。
     *          匿名身份过滤器，这个过滤器个人认为很重要，需要将它与UsernamePasswordAuthenticationFilter 放在一起比较理解，
     *          spring security为了兼容未登录的访问，也走了一套认证流程，只不过是一个匿名的身份。
     *      2.  匿名认证过滤器，可能有人会想：匿名了还有身份？
     *          个人对于Anonymous匿名身份的理解是Spirng Security为了整体逻辑的统一性，即使是未通过认证的用户，也给予了一个匿名身份。
     *          而AnonymousAuthenticationFilter该过滤器的位置也是非常的科学的，
     *          它位于常用的身份认证过滤器（如UsernamePasswordAuthenticationFilter、BasicAuthenticationFilter、RememberMeAuthenticationFilter）之后，
     *          意味着只有在上述身份过滤器执行完毕后，SecurityContext依旧没有用户信息，
     *          AnonymousAuthenticationFilter该过滤器才会有意义—-基于用户一个匿名身份。
     *   SessionManagementFilter
     *      securityContextRepository限制同一用户开启多个会话的数量
     *      SessionAuthenticationStrategy防止session-fixation protection attack（保护非匿名用户）
     *   ExceptionTranslationFilter
     *      ExceptionTranslationFilter异常转换过滤器位于整个springSecurityFilterChain的后方，用来转换整个链路中出现的异常
     *      此过滤器的作用是处理中FilterSecurityInterceptor抛出的异常，然后将请求重定向到对应页面，或返回对应的响应错误代码
     *   FilterSecurityInterceptor
     *      获取到所配置资源访问的授权信息
     *      根据SecurityContextHolder中存储的用户信息来决定其是否有权限
     *      主要一些实现功能在其父类AbstractSecurityInterceptor中
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //super.configure(http);

        http.authorizeRequests()
            .antMatchers("/initLoginPage", "/**/favicon.ico").permitAll()
            .anyRequest().authenticated();

        // https://blog.csdn.net/gtLBTNq9mr3/article/details/78139431
        http.csrf();
            //.csrfTokenRepository(new CookieCsrfTokenRepository());
            //.csrfTokenRepository(new HttpSessionCsrfTokenRepository());

        http.formLogin()
            .loginPage("/initLoginPage")
             //登录页面下表单提交的路径
            .loginProcessingUrl("/verifyLogin")
            .usernameParameter("k_username")
            .passwordParameter("k_password")
            .successForwardUrl("/welcome/home");

        http.rememberMe().rememberMeParameter("k_remember-me");

        //http.httpBasic();

    }


    @Bean(name="userDetailsService")
    UserDetailsService customUserDetailsService(){
        return new GeneralUserDetailsServiceImpl();
    }
}
